function [STRUCT] = matlabtetrisCustomized(varargin)
%MATLABTETRIS A MATLAB version of the classic game Tetris.
% The goal is to fill as many horizontal lines on the game board as 
% possible by manipulating the pieces as they fall into place.  As more
% lines are filled, gameplay speeds up.  More points are awarded the more
% lines are filled at once, and as a function of the current level.
%
% Pushing the following keys has the listed effect:
%
% Key     Effect
% ------------------
% n       Starts a new game in the middle of any game.
% p       Pauses/Unpauses game play.
% s       Starts the new game (alternative to pushing the start button).
%
% Other tips:
%
% To move the piece, use the arrow keys.
% The up arrows rotates a piece clockwise, shift+up, counter clockwise.
% Clicking on the preview window hides/unhides the preview (next piece).
% Click on the level (1) before starting a game to choose start level.  If
% the first level is too slow, try starting out at a higher level, up to 9. 
% The desired starting level may also be passed in as an argument when 
% first calling the game.  For example, 
%
%       matlabetetris(7)
%
% starts the game at level 7.
%
% Also, calling
%
%       A = matlabtetris;
%
% returns the handle/data structure.  Handles are lower-case, data 
% uppercase.  This can be useful for troubleshooting, etc.
%
% This game changes the state of the random generator.
% This game tries to write the high score to a file named:
%
%       TETRIS_HIGH_SCORE.mat
%
% Author: Matt Fig
% Date: 1/11/2012  
% Version 2.1 

boardRows = 25;

try
    rng('shuffle');  % So each game is not the same! RNG is new in r2011a.
catch  %#ok
    rand('twister',sum(100*clock));  %#ok Should work back to r2006a.
end

f_clr = [.741 .717 .42]; % Allows for easy change.  Figure color.

S.fig = figure('units','pixels',...
               'name','Tetris',...
               'menubar','none',...
               'numbertitle','off',...
               'position',[100 100 650 720],...
               'color',f_clr,...               
               'busyaction','cancel',...
               'renderer','opengl');

S.axs = axes('units','pix',...
                'position',[30 30 360 630],...
                'ycolor',f_clr,...
                'xcolor',f_clr,...
                'xtick',[],'ytick',[],...
                'xlim',[-1 11],...
                'ylim',[-1 boardRows],...
                'color',f_clr,...
                'visible','off'); % The main board
% Template positions for the patch objects (bricks) in both axes.
X = [0 .2 0;.2 .8 .2;.2 .8 .8;.8 .2 .8;1 .2 1;0 .2 1;0 .2 0]; %useful RL
Y = [0 .2 0;.2 .2 .2;.8 .8 .2;.8 .8 .8;1 .2 1;1 .2 0;0 .2 0];
g1 = repmat([.9 .65 .4],[1,1,3]); % Grey color used throughout.
S.PRVPOS{1} = [1.5 2.5 3.5 4.5;3 3 3 3]; % Positions of the previews. %useful RL
S.PRVPOS{2} = [2 3 3 4;2.5 2.5 3.5 2.5]; % 1-I,2-T,3-L,4-J,5-Z,6-S,7-O %useful RL
S.PRVPOS{3} = [2 3 4 4;2.5 2.5 2.5 3.5]; %useful RL
S.PRVPOS{4} = [2 2 3 4;3.5 2.5 2.5 2.5]; %useful RL
S.PRVPOS{5} = [2 3 3 4;3.5 3.5 2.5 2.5]; %useful RL
S.PRVPOS{6} = [2 3 3 4;2.5 2.5 3.5 3.5]; %useful RL
S.PRVPOS{7} = [2.5 2.5 3.5 3.5;3.5 2.5 3.5 2.5]; %useful RL

% Make the board boarders.
for jj = [-1 10]
    Xi = X + jj;
    
    for ii = -1:(boardRows-1)
        patch(Xi,Y+ii,g1,...
              'edgecolor','none',...
              'handlevis','callback') % Don't need these handles.
    end
end

% Making the bottom border line
for ii = 0:9
    patch(X+ii,Y-1,g1,'edgecolor','none','handlevis','callback')
end

S.pch = zeros(10,boardRows); % These hold the handles to the patches.

for jj = 0:(boardRows-1) % Make the board squares.
    for ii = 0:9        
        S.pch(ii+1,jj+1) = patch(X+ii,Y+jj,'w','edgecolor','w');        
    end
end
% Hold the colors of the pieces, and board index where each first appears.
S.PCHCLR = {reshape([1 .75 .5 0 0 0 0 0 0],1,3,3),...
            reshape([0 0 0 1 .75 .5 0 0 0],1,3,3),...
            reshape([0 0 0 0 0 0 1 .75 .5],1,3,3),...
            reshape([1 .75 .5 1 .75 .5 0 0 0],1,3,3),...
            reshape([1 .75 .5 0 0 0 1 .75 .5],1,3,3),...
            reshape([0 0 0 1 .75 .5 1 .75 .5],1,3,3),...
            reshape([.5 .25 0 .5 .25 0 .5 .25 0],1,3,3)}; % Piece colors.
% S.PCHIDX holds the location where each piece first appears on the board.
offset =30;
S.PCHIDX = {[194:197]+offset,[184 185 186 195]+offset,[184 185 186 196]+offset,...
            [184 185 186 194]+offset,[194 195 185 186]+offset,[184 195 185 196]+offset,...
            [185 186 195 196]+offset};

S.CURPRV = []; % Holds current preview patches. %useful RL
S.PRVNUM = []; % Holds the preview piece number, 1-7. %ueful RL May be not, confirm soon
S.BRDMAT = false(10,boardRows); % The matrix game board. % this holds the status %useful RL
S.CURROT = 1; % Holds the current rotation of the current piece. % useful RL
S.CURLVL = 1; % The current level.
S.CURLNS = 0; % The current number of lines % useful RL
% These next two dictate how fast the game increases its speed and also how
% many lines the player must score to go up a level, respectively.  The
% first value should be on (0,1].  Smaller values increase speed faster.
% No error handling is provided if you use bad values!

try
    SCR = load('TETRIS_HIGH_SCORE.mat');
    S.CURHSC = SCR.SCR; % The user has a previous High Score.
catch  %#ok
    S.CURHSC = 0;
end



set(S.fig,'name',['Tetris',' High Score - ', sprintf('%i',S.CURHSC)])

make_preview;
play_tet; % Initiate Gameplay.
while(1)
    [ip, ~] = getkey;
    if(ip == 'q')
        display('Quiting')
        close all
        return;
    end
    fig_kpfcn(ip);    
end

if nargout
    STRUCT = S; % Returns the structure if user requests.
end


    function [] = make_preview(varargin)
    % This function chooses which piece is going next and displays it.
    display('called make_preview')
        if nargin
            S.PRVNUM = varargin{1};
        else
            S.PRVNUM = ceil(rand*7); % Randomly choose one of the pieces.
        end

        if ~isempty(S.CURPRV)
            delete(S.CURPRV) % Delete previous preview.
        end
    end


    function [] = play_tet()
        display('called play_tet')
    % Picks a next piece and puts the preview in correct axes.
        S.PNM = S.PRVNUM; % Hold this for keypresfcn.
        S.CUR = S.PCHIDX{S.PRVNUM}; % Current loc. of current piece.
        S.COL = S.PCHCLR{S.PRVNUM}; % Transfer correct color.
        S.CURROT = 1; % And initial rotation number.
        set(S.pch(S.CUR),'facec','flat','cdata',S.COL,'edgecol','none')

        if any(S.BRDMAT(S.CUR))
            disp('....Game over....')
            clean_tet;  % Clean up the board.
            return
        else
            S.BRDMAT(S.CUR) = true; % Now update the matrix...
        end

        make_preview;  % Set up the next piece.        
    end


    function [] = game_step(varargin)
        display('called game_step')
    % Timerfcn, advances the current piece down the board
        col = ceil(S.CUR/10); % S.CUR defined in play_tet.
        row = rem(S.CUR-1,10) + 1;  % These are for the board matrix.
        minDistanceToTravel = boardRows;
        
        ur = unique(row);  % Check to see if we can drop it down
        
        for kk = 1:length(ur)
            
            minCol = min(col(row==ur(kk)));
            occCol = find(S.BRDMAT(ur(kk),1:minCol-1),1,'last');
            if(isempty(occCol))
                occCol = 0;
            end
            distanceToTravel = minCol - occCol;
            if(distanceToTravel<minDistanceToTravel)
                downShiftDistance = distanceToTravel;
                minDistanceToTravel = downShiftDistance;
            end
        end
        
        mover(-10*(minDistanceToTravel-1))  % O.k. to drop the piece... do it.

        col = ceil(S.CUR/10); % S.CUR defined in play_tet.
        row = rem(S.CUR-1,10) + 1;  % These are for the board matrix.

        check_rows;
        play_tet;        
    end


    function [] = fig_kpfcn(ip)
        display('called fig_kpfcn--')
        display(ip)
    % Figure (and pushbutton) keypressfcn
       

        if (ip == 's')
            game_step; % Just call another step.           
            return
        end

        col = ceil(S.CUR/10); % S.CUR defined in play_tet.
        row = rem(S.CUR-1,10) + 1;  % These index into board matrix.

        switch ip
            case 'd'
                % Without this IF, the piece will wrap around!
                if max(row)<=9
                    uc = unique(col);  % Check if object to the right.

                    for kk = 1:length(uc)
                        if (S.BRDMAT(max(row(col==uc(kk)))+1,uc(kk)))
                            
                            return
                        end
                    end

                    mover(1)   % O.k. to move.
                end
            case 'a'
                if min(row)>=2
                    uc = unique(col);  % Check if object to the left

                    for kk = 1:length(uc)
                        if (S.BRDMAT(min(row(col==uc(kk)))-1,uc(kk)))                            
                            return
                        end
                    end

                    mover(-1)  % O.k. to move.
                end
            case 'w'
                turner(row,col,0);  % Turn the piece.           
            otherwise
        end        
    end 


    function [] = mover(N)
        display('called mover')
    % Common task. Moves a piece on the board.
        S.BRDMAT(S.CUR) = false; % S.CUR, S.COL defined in play_tet.
        S.BRDMAT(S.CUR+N) = true; % All checks should be done already.
        S.CUR = S.CUR + N;
        set([S.pch(S.CUR-N),S.pch(S.CUR)],...
            {'facecolor'},{'w';'w';'w';'w';'flat';'flat';'flat';'flat'},...
            {'edgecolor'},{'w';'w';'w';'w';'none';'none';'none';'none'},...
            {'cdata'},{[];[];[];[];S.COL;S.COL;S.COL;S.COL})
    end


    function [] = turner(row,col,arg)
        display('called turner')
    % Common task. Rotates the pieces once at a time.
    % r is reading left/right, c is reading up/down.
    % For the switch:  1-I,2-T,3-L,4-J,5-Z,6-S,7-O
        switch S.PNM % Defined in play_tet.  Turn depends on shape.
            case 1  
                if any(col>(boardRows-1)) || all(col<=2)
                    return
                else
                    if S.CURROT == 1;
                        r = [row(2),row(2),row(2),row(2)];
                        c = [col(2)-2,col(2)-1,col(2),col(2)+1];
                        S.CURROT = 2;
                    elseif all(row>=9)
                        r = 7:10;
                        c = [col(2),col(2),col(2),col(2)];
                        S.CURROT = 1;
                    elseif all(row==1)
                        r = 1:4;
                        c = [col(2),col(2),col(2),col(2)];
                        S.CURROT = 1;
                    else
                        r = [row(2)-1,row(2),row(2)+1,row(2)+2];
                        c = [col(2),col(2),col(2),col(2)];
                        S.CURROT = 1;
                    end
                end
            case 2
                if sum(col==1)==3
                    return
                end

                if arg
                    S.CURROT = mod(S.CURROT+1,4)+1;
                end

                switch S.CURROT
                    case 1
                        r = [row(2),row(2),row(2),row(2)+1];
                        c = [col(2)-1,col(2),col(2)+1,col(2)];
                    case 2
                        if sum(row==1)==3
                            r = [1 2 3 2];
                            c = [col(2),col(2),col(2),col(2)-1];
                        else
                            r = [row(2)-1,row(2),row(2),row(2)+1];
                            c = [col(2),col(2),col(2)-1,col(2)];
                        end
                    case 3
                        r = [row(2)-1,row(2),row(2),row(2)];
                        c = [col(2),col(2),col(2)-1,col(2)+1];
                    case 4
                        if sum(row==10)==3
                            r = [9 9 8 10];
                            c = [col(2)+1,col(2),col(2),col(2)];
                        else
                            r = [row(2)-1,row(2),row(2),row(2)+1];
                            c = [col(2),col(2),col(2)+1,col(2)];
                        end
                end

                S.CURROT = mod(S.CURROT,4) + 1;
            case 3
                if sum(col==1)==3
                    return
                end

                if arg
                    S.CURROT = mod(S.CURROT+1,4)+1;
                end

                switch S.CURROT
                    case 1
                        r = [row(2),row(2),row(2),row(2)+1];
                        c = [col(2)+1,col(2),col(2)-1,col(2)-1];
                    case 2
                        if sum(row==1)==3
                            r = [1:3 1];
                            c = [col(2),col(2),col(2),col(2)-1];
                        else
                            r = [row(2)-1,row(2),row(2)-1,row(2)+1];
                            c = [col(2),col(2),col(2)-1,col(2)];
                        end
                    case 3
                        r = [row(2)-1,row(2),row(2),row(2)];
                        c = [col(2)+1,col(2),col(2)+1,col(2)-1];
                    case 4
                        if sum(row==10)==3
                            r = [10 9 10 8];
                            c = [col(2)+1,col(2),col(2),col(2)];
                        else
                            r = [row(2)-1,row(2),row(2)+1,row(2)+1];
                            c = [col(2),col(2),col(2),col(2)+1];
                        end
                end

                S.CURROT = mod(S.CURROT,4) + 1;
            case 4
                if sum(col==1)==3
                    return
                end

                if arg
                    S.CURROT = mod(S.CURROT+1,4)+1;
                end

                switch S.CURROT
                    case 1
                        r = [row(2),row(2),row(2),row(2)+1];
                        c = [col(2)-1,col(2),col(2)+1,col(2)+1];
                    case 2
                        if sum(row==1)==3
                            r = [1 2 3 3];
                            c = [col(2),col(2),col(2),col(2)-1];
                        else
                            r = [row(2)-1,row(2),row(2)+1,row(2)+1];
                            c = [col(2),col(2),col(2),col(2)-1];
                        end
                    case 3
                        r = [row(2)-1,row(2),row(2),row(2)];
                        c = [col(2)-1,col(2),col(2)-1,col(2)+1];
                    case 4
                        if sum(row==10)==3
                            r = [8 9 8 10];
                            c = [col(2)+1,col(2),col(2),col(2)];
                        else
                            r = [row(2)-1,row(2),row(2)-1,row(2)+1];
                            c = [col(2),col(2),col(2)+1,col(2)];
                        end
                end

                S.CURROT = mod(S.CURROT,4) + 1;
            case 5
                if any(col(2)>(boardRows-1)) || sum(col==1)==2
                    return
                elseif S.CURROT==1;
                    r = [row(2),row(2),row(2)-1,row(2)-1];
                    c = [col(2)+1,col(2),col(2),col(2)-1];
                    S.CURROT = 2;
                else
                    if sum(row==10)==2
                        r = [10 9 9 8];
                        c = [col(2)-1,col(2)-1,col(2),col(2)];
                    else
                        r = [row(2)-1,row(2),row(2),row(2)+1];
                        c = [col(2),col(2),col(2)-1,col(2)-1];
                    end

                    S.CURROT = 1;
                end
            case 6
                if any(col(2)>(boardRows-1))|| sum(col==1)==2
                    return
                elseif S.CURROT==1;
                    r = [row(2)+1,row(2),row(2)+1,row(2)];
                    c = [col(2)-1,col(2),col(2),col(2)+1];
                    S.CURROT = 2;
                else
                    if sum(row==1)==2
                        r = [1 2 2 3];
                        c = [col(2)-1,col(2)-1,col(2),col(2)];
                    else
                        r = [row(2)-1,row(2),row(2),row(2)+1];
                        c = [col(2)-1,col(2),col(2)-1,col(2)];
                    end
                    S.CURROT = 1;
                end
            otherwise
                return % The O piece.
        end

        ind = r + (c-1)*10; % Holds new piece locations.
        tmp = S.CUR; % Want to call SET last! S.CUR defined in play_tet.
        S.BRDMAT(S.CUR) = false;

        if any(S.BRDMAT(ind)) % Check if any pieces are in the way.
            S.BRDMAT(S.CUR) = true;
            return
        end

        S.BRDMAT(ind) = true;
        S.CUR = ind; % S.CUR, S.COL defined in play_tet.
        piece = S.PNM
        rot = S.CURROT
        set([S.pch(tmp),S.pch(ind)],...
            {'facecolor'},{'w';'w';'w';'w';'flat';'flat';'flat';'flat'},...
            {'edgecolor'},{'w';'w';'w';'w';'none';'none';'none';'none'},...
            {'cdata'},{[];[];[];[];S.COL;S.COL;S.COL;S.COL});
    end


    function [] = check_rows()
        display('called check_rows')
    % Checks if any row(s) needs clearing and clears it (them).
        TF = all(S.BRDMAT); % Finds the rows that are full.

        if any(TF)  % There is a row that needs clearing.
            sm = sum(TF); % How many rows are there?
            B = false(size(S.BRDMAT));  % Temp store to switcheroo.
            B(:,1:(boardRows-sm)) = S.BRDMAT(:,~TF);
            S.BRDMAT = B;
            TF1 = find(TF); % We only need to drop those rows above.
            L = length(TF1);
            TF = TF1-(0:L-1);
            S.CURLNS = S.CURLNS + L;
            
            for kk = 1:L % 'Delete' these rows.
                set(S.pch(:,TF(kk):(boardRows-1)),...
                    {'facecolor';'edgecolor';'cdata'},...
                    get(S.pch(:,TF(kk)+1:boardRows),...
                    {'facecolor';'edgecolor';'cdata'}));
            end
            
        end
    end

    function [] = clean_tet()
        display('called clean_tet')
        S.BRDMAT(:) = false; % Reset the board matrix.
    % Cleans up the board and board matrix after Game Over.
        for kk = 1:boardRows
            set(S.pch(:,kk),'cdata',g1,'edgecol','none')
            drawnow % Gives the nice effect of grey climbing up.
        end       
    end


end